package org.budo.support.application.registry;

import java.util.Timer;
import java.util.TimerTask;

import org.budo.support.http.client.HttpUtil;
import org.budo.support.http.client.Response;
import org.budo.support.lang.util.ProcessUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.spring.expression.util.SpelUtil;
import org.budo.support.spring.util.SpringUtil;
import org.budo.time.Time;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Getter
@Setter
@Slf4j
public abstract class AbstractBudoApplicationRegistry implements BudoApplicationRegistry, InitializingBean, DisposableBean {
    private static final String PATTERN = "yyyyMMdd.HHmmss.SSS";

    private Integer delay = 5 * 1000; // 延迟5秒启动

    private Integer period = 10 * 1000; // 每10秒心跳

    protected static final int EXPIRE = 30; // 30秒无心跳则过期

    private String applicationName;

    /**
     * 配置 nginx 地址
     */
    private String nginxUrl;

    private String host = "#{T(org.budo.support.java.net.util.JavaNetUtil).getHostInnerFirst()}";

    @Override
    public void afterPropertiesSet() throws Exception {
        this.scheduleHeartbeatTimerTask();
    }

    @Override
    public void destroy() throws Exception {
        this.unregisterApplications();
    }

    protected void scheduleHeartbeatTimerTask() {
        log.info("#54 scheduleHeartbeatTimerTask, this=" + this);

        Timer timer = new Timer("AbstractBudoAppRegistry-Timer-" + ProcessUtil.getCurrentProcessName());
        TimerTask timerTask = new TimerTask() {
            public void run() {
                registerApplications();
            }
        };

        timer.schedule(timerTask, this.getDelay(), this.getPeriod());
    }

    private void registerApplications() {
        try {
            String ip = SpelUtil.expression(this.getHost()).getValue() + "";

            this.registerApp(ip);
        } catch (Throwable e) {
            log.error("#42 writeAppInfo error, e=" + e, e);
        }
    }

    protected void unregisterApplications() {
        try {
            String ip = SpelUtil.expression(this.getHost()).getValue() + "";

            this.unregisterApp(ip);
        } catch (Throwable e) {
            log.error("#42 writeAppInfo error, e=" + e, e);
        }
    }

    protected void registerApp(String ip) {
        if (!SpringUtil.isActive()) {
            log.warn("#56 register return, spring is not active, ip={}, this={}, applicationContext={}", ip, this, SpringUtil.getApplicationContext());
            return;
        }

        log.debug("#56 register, ip={}, this={}", ip, this);

        Integer port = SpringUtil.getPropertyValueInteger("server.port", 8080);
        String contextPath = SpringUtil.getPropertyValue("server.servlet.context-path");

        String startTime = Time.when(ProcessUtil.getStartTime()).toString(PATTERN);

        String now = Time.now().toString(PATTERN);
        Integer currentProcessId = ProcessUtil.getCurrentProcessId();
        String currentProcessName = ProcessUtil.getCurrentProcessName();

        String name = this.applicationName();

        BudoApplication application = new BudoApplication() //
                .setName(name) //
                .setIp(ip) //
                .setPort(port) //
                .setContextPath(contextPath) //
                .setCreateAt(now) //
                .setProcessId(currentProcessId) //
                .setProcessName(currentProcessName) //
                .setStartAt(startTime);

        this.register(application);

        // 生成 upstream 推送到 nginx
        this.pushNginxUpstream();
    }

    protected void unregisterApp(String ip) {
        Integer port = SpringUtil.getPropertyValueInteger("server.port", 8080);

        String name = this.applicationName();

        BudoApplication application = new BudoApplication() //
                .setName(name) //
                .setIp(ip) //
                .setPort(port);

        this.unregister(application);

        // 生成 upstream 推送到 nginx
        this.pushNginxUpstream();
    }

    protected String applicationName() {
        String name = this.getApplicationName();
        if (null == name) {
            name = BudoApplicationUtil.getApplicationName();
        }

        return name;
    }

    protected void pushNginxUpstream() {
        this.compare_and_push_nginx_upstream();
    }

    /**
     * 如果有变化 就推上去
     */
    protected void compare_and_push_nginx_upstream() {
        if (null == this.getNginxUrl() || this.getNginxUrl().trim().isEmpty()) {
            log.warn("#154 compare_and_push_nginx_upstream, this.nginxUrl=" + this.getNginxUrl() + ", this=" + this);
            return;
        }

        ApplicationContext applicationContext = SpringUtil.getApplicationContext();
        if (!SpringUtil.isActive(applicationContext)) {
            String processName = ProcessUtil.getCurrentProcessName();
            log.warn("#161 compare_and_push_nginx_upstream, spring no active, processName={}, this={}, applicationContext={}", processName, this, applicationContext);
            return;
        }

        // 读取当前 nginx_upstream
        Response upstream_read = HttpUtil.post(this.getNginxUrl() + "/budo-nginx/upstream-read");
        String upstream_local = BudoApplicationUtil.upstream(applicationContext, false);
        if (StringUtil.trimEquals(upstream_read, upstream_local)) {
            log.debug("#169 nginx_upstream_read={}, upstream_local={}", upstream_read, upstream_local);
            return;
        }

        // requestBody 上传
        Response upstream_write = HttpUtil.post(this.getNginxUrl() + "/budo-nginx/upstream-write", upstream_local.getBytes());
        log.info("#174 upstream_write=" + upstream_write + ", upstream_read=" + upstream_read + ", upstream_local=" + upstream_local); // 有变化 上传
    }
}